#include <jni.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <android/log.h>
#include <stdlib.h>

// 引入自动生成的JNI头文件
#include "com_example_ndkjnidemo_home_staticregist_JNIMethodStatic.h"
// 引入日志工具类
#include "log-util.h"

//
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

using namespace std;

//
static const char *const kClassJniTest = "com/example/ndkjnidemo/home/dynamicregist/JNIMethodDynamic";
static const char *const kClassJniExtern = "com/example/ndkjnidemo/home/staticregist/ExternClass";

//保存java层的fieldID 和methodID 以方便后续使用
struct fields_t {
    jfieldID context;
    jfieldID name;
    jclass clazz;
    jobject obj;//obj的保存一定要使用NewGlobalRef的方法
    jmethodID setNameMethodID;
    jmethodID getNameMethodID;
    jmethodID transimFromJNIMethodID;
} fields;
struct fields_t *Pfield;

/**
 *
 * @param env
 * @param obj
 * @return
 */
jstring JNIMethodDynamic_stringFromJNI(JNIEnv *env, jobject obj) {
    LOGI("JAVA 调用 JNI stringFromJNI");
    string hello = "Hello from JNI 动态注册";
    return env->NewStringUTF(hello.c_str());
}

/**
 *
 * @param env
 * @param thizz
 * @param information
 * @param obj
 */
void JNIMethodDynamic_transmitToJNI(JNIEnv *env, jobject thizz, jstring information, jobject obj) {
    const char *transmitString = env->GetStringUTFChars(information, NULL);
    LOGI("JAVA 调用 JNI transmitToJNI transmitString=%s", transmitString);
    Pfield->obj = env->NewGlobalRef(thizz);//想持久保存thizz的对象，一定要使用NewGlobalRef 后续也要手动删除
    //初始化  field id 获取obj 的name
    jclass clazz = env->FindClass(kClassJniExtern);
    jfieldID fieldNameID = env->GetFieldID(clazz, "className", "Ljava/lang/String;");
    if (fieldNameID == NULL) {
        LOGE("can not find className ");
        return;
    }

    jobject objName;
    objName = env->GetObjectField(obj, fieldNameID);
    jstring jstringName = (jstring) (objName);
    const char *charName = env->GetStringUTFChars(jstringName, NULL);
    LOGI("JNI 调用 JAVA  ExternClass className=%s", charName);


    //调用java 层的方法。CallVoidMethod CallObjectMethod
    string message = "JNI 调用 JAVA ";
    jstring jMessage = env->NewStringUTF(message.c_str());
    env->CallVoidMethod(Pfield->obj, Pfield->setNameMethodID, jMessage);

    jobject callName = env->CallObjectMethod(Pfield->obj, Pfield->getNameMethodID);
    const char *callNameChar = env->GetStringUTFChars((jstring) callName, NULL);
    LOGI("JNI 调用 JAVA  getName=%s", callNameChar);
    jobject calltransim = env->CallObjectMethod(Pfield->obj, Pfield->transimFromJNIMethodID,
                                                jMessage, obj);

    const char *calltransimChar = env->GetStringUTFChars((jstring) calltransim, NULL);
    LOGI("JNI 调用 JAVA transimFromJNI calltransimChar=%s", calltransimChar);

    //垃圾回收
    env->DeleteLocalRef(callName);
    env->DeleteLocalRef(calltransim);
    env->DeleteLocalRef(objName);
    env->DeleteGlobalRef(Pfield->obj);
}

/**
 *
 * @param env
 * @param thizz
 */
void JNIMethodDynamic_native_init(JNIEnv *env, jobject thizz) {
    LOGI("JAVA 调用 JNI  native_1init");
    //初始化 方法 field id
    Pfield = &fields;
    jclass clazz = env->FindClass(kClassJniTest);
    Pfield->clazz = clazz;
    if (Pfield->clazz == NULL) {
        LOGE("can not find class");
        return;
    }

    Pfield->name = env->GetFieldID(clazz, "name", "Ljava/lang/String;");
    if (Pfield->name == NULL) {
        LOGE("can not find name ID");
        return;
    }

    Pfield->context = env->GetFieldID(clazz, "testContext", "Landroid/content/Context;");
    if (Pfield->context == NULL) {
        LOGE("can not find context ID");
        return;
    }

    Pfield->setNameMethodID = env->GetMethodID(
            clazz,
            "setName",
            "(Ljava/lang/String;)V");
    if (Pfield->setNameMethodID == NULL) {
        LOGE("can not find setNameMethodID");
        return;
    }

    Pfield->getNameMethodID = env->GetMethodID(
            clazz,
            "getName",
            "()Ljava/lang/String;");

    if (Pfield->getNameMethodID == NULL) {
        LOGE("can not find getNameMethodID");
        return;
    }

    Pfield->transimFromJNIMethodID = env->GetMethodID(
            clazz,
            "transimFromJNI",
            "(Ljava/lang/String;Lcom/example/ndkjnidemo/home/staticregist/ExternClass;)Ljava/lang/String;");

    if (Pfield->transimFromJNIMethodID == NULL) {
        LOGE("can not find transimFromJNIMethodID");
        return;
    }
}

/**
 *
 */
static const JNINativeMethod gMethods[] = {
        {
                "native_init",
                "()V",
                (void *) JNIMethodDynamic_native_init
        },

        {
                "transmitToJNI",
                "(Ljava/lang/String;Lcom/example/ndkjnidemo/home/staticregist/ExternClass;)V",
                (void *) JNIMethodDynamic_transmitToJNI
        },

        {
                "stringFromJNI",
                "()Ljava/lang/String;",
                (void *) JNIMethodDynamic_stringFromJNI
        },
};

/**
 *
 * @param env
 * @param className
 * @param gMethod
 * @param numMethods
 * @return
 */
static int registerNativeMethods(JNIEnv *env, const char *className, const JNINativeMethod *gMethod,
                                 int numMethods) {
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        LOGI(" JNI registerNativeMethods 注册失败，没发现此类");
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethod, numMethods) < 0) {
        LOGI(" JNI registerNativeMethods 注册失败");
        return JNI_FALSE;
    }
    LOGI(" JNI registerNativeMethods 注册成功");
    return JNI_TRUE;
}

/**
 *
 * @param env
 * @return
 */
static int register_along_jni(JNIEnv *env) {
    LOGI(" JNI registerNativeMethods 注册");
    return registerNativeMethods(env, kClassJniTest, gMethods,
                                 NELEM(gMethods));
}

/**
 * 当系统加载动态库的时候（在java中就是System.loadLibrary()）由JNI调用。
 * 全局只能存在一个
 * @param vm
 * @return
 */
//JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /* reserved */) {
//    LOGI(" JNI_OnLoad 加载");
//    JNIEnv *env = NULL;
//    jint result = -1;
//    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
//        LOGE("ERROR: JNI_OnLoad 版本错误");
//
//        return JNI_ERR;
//    }
//    if (register_along_jni(env) == -1) {
//        LOGE("ERROR:  JNI_OnLoad 加载失败");
//        return JNI_ERR;
//    }
//    result = JNI_VERSION_1_6;
//    return result;
//}